package com.iwdnb.gkgz.application.service.impl;

import java.util.ArrayList;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.Executor;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.function.Function;
import java.util.stream.Collectors;

import cn.hutool.core.date.DateUtil;
import com.google.common.collect.Lists;
import com.iwdnb.bmnf.common.service.CacheService;
import com.iwdnb.bmnf.common.utils.BeanConvertUtils;
import com.iwdnb.gkgz.application.service.StockDayDataSerivce;
import com.iwdnb.gkgz.application.service.StockLimitUpService;
import com.iwdnb.gkgz.application.service.StockPoolService;
import com.iwdnb.gkgz.application.service.StockService;
import com.iwdnb.gkgz.application.service.StrategyTradeService;
import com.iwdnb.gkgz.common.model.dto.Stock;
import com.iwdnb.gkgz.common.model.dto.StockDayData;
import com.iwdnb.gkgz.common.model.dto.StockFenshiData;
import com.iwdnb.gkgz.common.model.dto.StockLimitUpData;
import com.iwdnb.gkgz.common.utils.RealtimeStockUtils;
import com.iwdnb.gkgz.common.utils.StockBlackListUtils;
import com.iwdnb.gkgz.common.utils.StockFenshiUtils;
import com.iwdnb.gkgz.common.utils.StockUtils;
import com.iwdnb.gkgz.common.utils.WorkTimeUtils;
import com.iwdnb.gkgz.infrastructure.dao.model.query.StockLimitUpQuery;
import com.iwdnb.gkgz.infrastructure.dao.repo.StockDayDataRepository;
import com.iwdnb.gkgz.infrastructure.dao.repo.StockFenshiRepository;
import com.iwdnb.gkgz.infrastructure.dao.repo.StockLimitUpRepository;
import com.iwdnb.gkgz.infrastructure.dao.repo.StockRepository;
import com.iwdnb.wwzy.dao.MetaDataDAO;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.collections4.CollectionUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

@Service
@Slf4j
public class StockDayDataSerivceImpl implements StockDayDataSerivce {
    @Autowired
    private StockService stockService;
    @Autowired
    private StockPoolService stockPoolService;
    @Autowired
    private StockLimitUpService stockLimitUpService;
    @Autowired
    private StockDayDataRepository stockDayDataRepository;
    @Autowired
    private StockLimitUpRepository stockLimitUpRepository;
    @Autowired
    private StockRepository stockRepository;
    @Autowired
    private StrategyTradeService strategyTradeService;
    @Autowired
    private StockFenshiRepository stockFenshiRepository;
    @Autowired
    private MetaDataDAO metaDataDAO;
    @Autowired
    private Executor executor;
    @Autowired
    private CacheService cacheService;

    @Override
    public void loadAllStockDayData(String beginDate, String endDate, boolean loadFenshiFlag, boolean loadLimitUpFlag) {
        beginDate = DateUtil.formatDate(DateUtil.parseDate(beginDate)) + " 00:00:00";
        endDate = DateUtil.formatDate(DateUtil.parseDate(endDate)) + " 23:59:59";
        List<Stock> stockList = stockService.queryStockList();
        List<String> codes = stockList.stream()
            .map(stock -> stock.getCode()).collect(Collectors.toList());
        codes = StockBlackListUtils.getCanBuyCodeList(codes);
        int size = codes.size();
        Date date = DateUtil.parseDateTime(DateUtil.formatDateTime(new Date()));
        if (loadFenshiFlag && DateUtil.parseDateTime(beginDate).before(date)
            && DateUtil.parseDateTime(endDate).after(date)) {
            loadFenshiFlag = true;
        }
        boolean finalLoadFenshiFlag = loadFenshiFlag;
        String finalBeginDate = beginDate;
        String finalEndDate = endDate;
        for (int i = 0; i < codes.size(); i++) {
            String c = codes.get(i);
            int finalIndex = i;
            executor.execute(() -> {
                List<StockDayData> stockDayDataList = loadStockDayData(c, finalBeginDate, finalEndDate);
                log.info("{}-{}同步K线数据成功", c, finalIndex);
                if (finalLoadFenshiFlag) {
                    List<StockDayData> fenshiList = StockFenshiUtils.getFenshiList(c);
                    StockFenshiData stockFenshiData = new StockFenshiData();
                    stockFenshiData.setCode(c);
                    stockFenshiData.setDate(date);
                    stockFenshiData.setFenshiList(fenshiList);
                    if (CollectionUtils.isNotEmpty(fenshiList)) {
                        String beforeDate = DateUtil.formatDate(DateUtil.offsetDay(date, -1));
                        StockDayData data = StockUtils.getStockDayData(stockDayDataList, beforeDate);
                        if (Objects.nonNull(data)) {
                            fenshiList.get(0).setBeforeClosePrice(data.getClosePrice());
                        }
                    }
                    stockFenshiRepository.save(stockFenshiData);
                    log.info("{}-{}同步分时数据成功", c, finalIndex);
                }
                if (finalIndex == size - 1 && loadLimitUpFlag) {
                    loadAllLimitUpData(finalBeginDate);
                }
            });
        }
    }

    @Override
    public List<StockDayData> loadStockDayData(String code, String startDate, String endDate) {
        List<StockDayData> list = stockDayDataRepository.queryAll(code);
        if (CollectionUtils.isEmpty(list)) {
            list = new ArrayList<>();
        }
        List<StockDayData> stockDayDataList = StockUtils.getHistoryData(code, startDate, endDate);
        if (CollectionUtils.isEmpty(stockDayDataList)) {
            return null;
        }
        for (StockDayData stockDayData : stockDayDataList) {
            String date = stockDayData.getDate();
            StockDayData old = StockUtils.getStockDayData(list, date);
            if (Objects.nonNull(old)) {
                BeanConvertUtils.copyProperties(stockDayData, old);
            } else {
                list.add(stockDayData);
            }
        }
        stockDayDataRepository.saveAll(code, list);
        return list;
    }

    @Override
    public List<StockDayData> queryStockDayDataList(String code) {
        return stockDayDataRepository.queryAll(code);
    }

    @Override
    public List<StockDayData> queryStockDayDataListBeforeDate(String code, String date) {
        return stockDayDataRepository.queryAllBeforeDate(code, date);
    }

    @Override
    public void loadAllLimitUpData(String date) {
        log.info("{}-loadAllLimitUpData start.", date);
        List<Stock> stockList = stockService.queryStockList();
        List<String> codes = StockUtils.formatStockCodes(stockList);
        codes = StockBlackListUtils.getCanBuyCodeList(codes);
        StockLimitUpQuery query = new StockLimitUpQuery();
        query.setDateBegin(DateUtil.formatDateTime(DateUtil.beginOfDay(DateUtil.parseDate(date))));
        query.setDateEnd(DateUtil.formatDateTime(DateUtil.endOfDay(new Date())));
        List<StockLimitUpData> stockLimitUpDataList = stockLimitUpRepository.queryLimitUpList(query);
        Map<String, StockLimitUpData> stockLimitUpDataMap = stockLimitUpDataList.stream().collect(
            Collectors.toMap(t -> t.getCode() + "_" + DateUtil.formatDate(t.getDate()), Function.identity()));
        AtomicInteger count = new AtomicInteger(0);
        for (String code : codes) {
            executor.execute(() -> {
                List<StockDayData> stockDayDataList = stockDayDataRepository.queryAllByDateAfter(code, date);
                if (CollectionUtils.isEmpty(stockDayDataList)) {
                    return;
                }
                List<StockLimitUpData> limitUpDataList = new ArrayList<>();
                for (StockDayData stockDayData : stockDayDataList) {
                    if (!StockUtils.isLimitUp(stockDayData)) {
                        continue;
                    }
                    StockLimitUpData data = stockLimitUpDataMap.get(code + "_" + stockDayData.getDate());
                    if (Objects.nonNull(data)) {
                        continue;
                    }
                    data = BeanConvertUtils.convert(stockDayData, StockLimitUpData.class);
                    data.setDate(DateUtil.parseDate(stockDayData.getDate()));
                    data.setCode(code);
                    data.init();
                    limitUpDataList.add(data);
                }
                if (CollectionUtils.isNotEmpty(limitUpDataList)) {
                    stockLimitUpRepository.batchSaveStockLimitUpData(limitUpDataList);
                }
                int c = count.incrementAndGet();
                log.info("完成涨停板初始化数据:{},本次{}条数据.", c, limitUpDataList.size());
            });
        }
    }

}
